<?php

/**
 * <?php
 * ����� �������������� � �������� ������ 
 * Example
 * <code>
 * //php
 * if(!class_exists('AutosaverFiles')){
 * 	include_once(dirname(__FILE__) . '/AutosaverFiles.class.php');
 * }
 * $autosave = new AutosaverFiles();//������������� �������
 * $autosave->setActiveTemplate("<b>", "</b>");//��������� ������� ������ ����. � ������ ������ ���� �������� � ��� B. �� ������� � ��� A.
 * 
 * if(sizeof($_FILES) > 0){
 * 	$autosave->autosave(dirname(__FILE__) . '/tmp/'); //�������� ����� ����� ������ �� ������ ����� POST �������.
 * 	
 * 	$autosave->moveAutosavedFiles(dirname(__FILE__) . '/dir/');//�������� ����� ����� ���������� �� ������������ � �� ����� ��������� �� ���������� ��������
 * }
 * else {
 * 	$autosave->putSavedFiles('tempName_a', 'Some/Path/To/File');//��������� ����� ����������� ������(��� �����������) ����� � input type=file
 * }
 * ?>
 * //HTML
 * <form method="POST" enctype="multipart/form-data">
 * 
 * 	<input type="file" name="tempName_a">
 * 	<input type="submit" value="send">
 * 	<?php
 * 		echo $autosave->writeStateInHtml('tempName_a');//����� � HTML �� ����� ����
 * 		echo $autosave->getFilePath('tempName_a');//��������� ���� � ����� �� ����� ����
 * 	?>
 * 
 * </form>
 * </code>
*/



if(!class_exists('AutosaverFilesInterface')){
	include_once(dirname(__FILE__) . '/AutosaverFilesInterface.php');
}
if(!class_exists('FileNameGenerator')){
	include_once(dirname(__FILE__) . '/FileNameGenerator.php');
}

class AutosaverFiles implements AutosaverFilesInterface {
	
	public static $AUTOSAVER_FILES_ALEREADY_SAVED = 'alreadySaved';
	public static $AUTOSAVER_FILES_AUTOSAVED = 'autosaved';
	public static $AUTOSAVER_FILES_MOVED = 'moved';
	
	private $_excludedFieldsInFiles = array();
	private $_alreadySavedFiles = array();
	private $_autosavedFiles = array();
	private $_movedFiles = array();
	private $_allowedExtentions = array(
		'.rar', 
		'.zip', 
		'.7zip', 
		'.jpg', 
		'.jpeg',
		'.JPEG',
		'.gif',
		'.png', 
		'.docx', 
		'.doc', 
		'.xlsx', 
		'.xls'
		);
	//��� ������ � �����
	private $_inHtmlBegin = "<a target='_blank' href='";	
	private $_inHtmlEnd = "'>link</a>";	
		
	private $_nameGenerator;
	
	private $_temporaryDir;
	private $_pathToSave;
	
	public function __construct($pathToSave, $tmpDir){
		
		if($pathToSave == ''){
			throw new Exception("pathToSave must by not empty.");
		}
		if($tmpDir == ''){
			throw new Exception("tmpDir must by not empty.");
		}
		$this->_pathToSave = $pathToSave;
		$this->_temporaryDir = $tmpDir;
		$this->_nameGenerator = new FileNameGenerator();
		
		if(sizeof($_FILES) > 0){
			$this->autosave($this->_temporaryDir); //�������� ����� ����� ������ �� ������ ����� POST �������.	
		}
	}
	/**
	 * ������� ���� ���� => �������� � ������ ����������� ������.
	 * 
	 * ����� � ������ ������� ��� �������������� ��������� ��� ������������� FormGenerator ��� ������ ������� $_FILES.
	 * ��� ���� ���������� getFilePath() ��� writeStateInHtml() ��� ����� ������� �� ����������� � ���������.
	 *
	 * @param string $field
	 * @param string $value
	 * 
	 * @return $this
	 */
	public function putSavedFiles($field, $value){
		
		if(!is_string($field) || !is_string($value)){
			throw new Exception("Illegal Arguments");
		}
		
		$this->_alreadySavedFiles[$field] = $value;
		return $this;
	}
	
	/**
	 * ��������� ���� �� ���������
	 *
	 * @param unknown_type $excludeField
	 */
	public function excludeField($excludeField){
		
		if(!is_string(excludeField)){
			throw new Exception("Illegal Argument.");
		}
		
		if(!is_array($excludeField)){
			$this->_excludedFieldsInFiles[] = $excludeField;
		}
		return $this;
	}
	
	/**
	 * ��������� ����� �� ������� $_FILES �� ��������� ���������
	 *
	 */
	public function autosave($pathToTemporaryDir){
		
		if(!is_string($pathToTemporaryDir) || $pathToTemporaryDir == ''){
			
			throw new Exception("Illegal Argument");
		}
		
		if(!AutosaverFiles::mkdirRecursive($pathToTemporaryDir)){
			throw new Exception("Cannot create folder from path " . $pathToTemporaryDir);
		}
		
		$this->_temporaryDir = $pathToTemporaryDir;
		
		foreach ($_FILES as $field => $file) {
			
			if(in_array($field, $this->_excludedFieldsInFiles)){
				continue;
			}
			
			
			if(isset($_POST[$field . 'FfAutoSaved']) && $file['name'] == ''){
				if(file_exists($this->_pathToSave . $_POST[$field . 'FfAutoSaved'])){
					$this->_alreadySavedFiles[$field] = $this->_pathToSave . $_POST[$field . 'FfAutoSaved'];
					continue;
				}
				elseif(file_exists($this->_temporaryDir . $_POST[$field . 'FfAutoSaved'])){
					$this->_autosavedFiles[$field] = $this->_temporaryDir . $_POST[$field . 'FfAutoSaved'];
					continue;
				}
			}
			
			if($file['name'] == ''){
				continue;
			}
			
			$tmp = explode('.', $file['name']);
			$ext = $tmp[sizeof($tmp) - 1];
			
			if(!in_array(strtolower('.' . $ext), $this->_allowedExtentions)){
				//trigger_error("Illegal file extention {$ext}. File skiped", E_USER_WARNING);
				continue;
			}
			
			if($file['tmp_name'] != ''){
				
				$tmpName = $this->_nameGenerator->getTemporaryName($field, $file);
				
				if(!copy($_FILES[$field]['tmp_name'], $pathToTemporaryDir . $tmpName)){
					throw new Exception("Cannot move file in temporary directory.");
				}
				if(file_put_contents($pathToTemporaryDir . $tmpName . '.ser', serialize($file)) === false){
					throw new Exception("Cannot write file in temporary directory.");
				}
				$this->_autosavedFiles[$field] = $pathToTemporaryDir . $tmpName;
			}
		}
		
		
	}
	
	/**
	 * ���������� ���� � ������������ (��������� ��� ��������) �����. ��� null ���� ���� �� ��������.
	 *
	 * @param string $fieldName
	 * @return string or null
	 */
	public function getFilePath($fieldName){
		
		if(array_key_exists($fieldName, $this->_autosavedFiles)){
			return $this->_autosavedFiles[$fieldName];
		}
		
		if(array_key_exists($fieldName, $this->_alreadySavedFiles)){
			return $this->_alreadySavedFiles[$fieldName];
		}
		
		if(array_key_exists($fieldName, $this->_movedFiles)){
			return $this->_movedFiles[$fieldName];
		}
		return  null;
	}
	
	/**
	 * ���������� ��� ������� � ������� �������� ���� � ����� ��� null ���� �� ������.
	 *
	 * ����� ���� � ���������� � getFilePath
	 * @param string $fieldName
	 * @return string or null
	 */
	public function getFileArray($fieldName){
		
		if(array_key_exists($fieldName, $this->_autosavedFiles)){
			return AutosaverFiles::$AUTOSAVER_FILES_ALEREADY_SAVED;
		}
		
		if(array_key_exists($fieldName, $this->_autosavedFiles)){
			return AutosaverFiles::$AUTOSAVER_FILES_AUTOSAVED;
		}
		
		if(array_key_exists($fieldName, $this->_movedFiles)){
			return AutosaverFiles::$AUTOSAVER_FILES_MOVED;
		}
		return null;
	}
	
	
	/**
	 * ���������� ����������� �� ��������� ���������� ����� � ����������.
	 * ��������� ������ � ������ ������������ ���������� ����������� �������.
	 * 
	 * ����� ������<b> ������ ����������� ����� ������ autosave()</b>. ��� ������ ������ - ������ ���������� �� �����.
	 *
	 * @param unknown_type $pathToDir
	 */
	public function moveAutosavedFiles(){
		
		$pathToDir = $this->_pathToSave;
		
		if(!is_string($pathToDir)){
			throw new Exception('Illegal Argument');
		}
				
		if(!AutosaverFiles::mkdirRecursive($pathToDir, 0755)){
			throw new Exception('Cannot create path ' . $pathToDir);
		}
		
		foreach ($this->_autosavedFiles as $field => $filePath){
			$fileInfo = unserialize(file_get_contents($filePath . '.ser'));
			
			$name = $this->_nameGenerator->getFixedName($field, $fileInfo);
			$savedPath = $pathToDir . $name;
			
			$this->saveFile($filePath, $savedPath, $field);
		}
		
		/*foreach ($this->_alreadySavedFiles as $field => $filePath){
			if(file_exists($filePath . '.ser') && file_exists($filePath)){
				
				$fileInfo = unserialize(file_get_contents($filePath . '.ser'));
			
				$savedPath = $pathToDir . $this->_nameGenerator->getFixedName($field, $fileInfo);
				
				if(!copy($filePath, $savedPath)){
					throw new Exception('Error save file');
				}
				else {
					unlink($filePath);
					unlink($filePath . '.ser');
					$this->_movedFiles[$field] = $savedPath;
					unset($this->_alreadySavedFiles[$field]);					
				}
			}
		}*/
		
	}
	
	public function moveAutosavedFile($field){
		
		$pathToDir = $this->_pathToSave;
		
		if(!is_string($pathToDir)){
			throw new Exception('Illegal Argument');
		}
				
		if(!AutosaverFiles::mkdirRecursive($pathToDir, 0755)){
			throw new Exception('Cannot create path ' . $pathToDir);
		}
		
		if(array_key_exists($field, $this->_autosavedFiles)){
			
			$filePath = $this->_autosavedFiles[$field];
			
			if(file_exists($filePath . '.ser')){
				$fileInfo = unserialize(file_get_contents($filePath . '.ser'));
				
				$name = $this->_nameGenerator->getFixedName($field, $fileInfo);
				$savedPath = $pathToDir . $name;
				
				$this->saveFile($filePath, $savedPath, $field);
			}
		}
	}

	
	public function saveFile($filePath, $savedPath, $field){
		
		if(!copy($filePath, $savedPath)){
			throw new Exception('Error save file "'.$filePath.'" to "'.$savedPath.'"' );
		}
		else {
			unlink($filePath);
			unlink($filePath . '.ser');
			$this->_movedFiles[$field] = $savedPath;
			$this->_alreadySavedFiles[$field] = $savedPath;
			unset($this->_autosavedFiles[$field]);
		}
	}
	
	/**
	 * ����� ��� ����� � ������� FormGenerator
	 *
	 * @param string $fieldName
	 * @return string &lt;Input type=hidden&gt
	 */
	public function writeStateInHtml($fieldName){
		
		if(!is_string($fieldName)){
			throw new Exception('Illegal Argument');
		}
		
		$tmp = $this->getFilePath($fieldName);
		if(!is_null($tmp)){
			
			$tmpUnix = str_replace("\\", '/', $tmp);//for Win
			
			if(strpos($tmpUnix, $_SERVER['DOCUMENT_ROOT']) !== false){
				$tmp = str_replace($_SERVER['DOCUMENT_ROOT'], '', $tmpUnix);
			}
			
			if($this->getFileArray($fieldName) != AutosaverFiles::$AUTOSAVER_FILES_MOVED){
				
				return "<input type='hidden' name='{$fieldName}FfAutoSaved' value='" . $this->getFileNameByPath($tmp) . "'>
					{$this->_inHtmlBegin}{$tmp}{$this->_inHtmlEnd}
					";
			}
			else {
				return "<input type='hidden' name='{$fieldName}FfAutoSaved' value='" . $this->getFileNameByPath($tmp) . "'>
				{$this->_inHtmlBegin}{$tmp}{$this->_inHtmlEnd}";
			}
			
		}
		else {
			return '';
		}
	}
	
	private function getFileNameByPath($path){
		
		$tmp = str_replace("\\", '/', $path);//For Win
		$tmp = explode('/', $tmp);
		
		return $tmp[sizeof($tmp) - 1];
	}
	
	/**
	 * ���������� ������ � ������������ � �������������� � ���������� ������������
	 *
	 * @return array
	 */
	public function getAllowedExtentions(){
		return $this->_allowedExtentions;
	}
	
	/**
	 * �������������  ������ � ������������ � �������������� � ���������� ������������
	 *
	 * @param array
	 */
	public function setAllowedExtentions($exts){
		if(!is_array($exts)){
			throw new Exception("Illegal Argument");
		}
		$this->_allowedExtentions = $exts;
	}
	
	/**
	 * ������������� ����� ����������� ����� ������
	 *
	 * @param unknown_type $nameGeneratorObject
	 */
	public function setNameGenerator($nameGeneratorObject){
		if(!($nameGeneratorObject instanceof NameGeneratorInterface)) {
			throw new Exception('Class must implement NameGeneratorInterface');
		}
		$this->_nameGenerator = $nameGeneratorObject;
	}
	
	/**
	 * ������������� HTML ������ ��� ������ ������������ �����
	 *
	 * @param string $HTML_Template
	 * @param string $HTML_TemplateEnd
	 * @return this
	 */
	public function setActiveTemplate($HtmlString, $HtmlStringEnd = ''){
		
		$this->_inHtmlBegin = $HtmlString;
		$this->_inHtmlEnd = $HtmlStringEnd;
		
		return $this;
	}
	
	/**
	 * ������� ����� �� ���������� ����, ������� �������� �������������.
	 *
	 * @param string $path
	 * @param int $mode
	 * @param string $beginPath
	 * @return boolean
	 */
	public static function mkdirRecursive($path, $mode = 0775, $beginPath = ''){
	
		if($beginPath != '' && substr($beginPath, -1) != '/'){
			throw new Exception("Incorrcet begin path. Must by ended by /");
		}
		
		if(is_dir($path)){
			return true;
		}
		
		$path = str_replace("\\", '/', $path);
		
		$pathArray = explode('/', $path);
				
		foreach ($pathArray as $val){
			
			if($val == ''){
				continue;
			}
			
			if(!file_exists($beginPath . $val)){
				if(!mkdir($beginPath . $val, $mode)){
					return false;
				}
			}
			$beginPath .= $val . '/';
		}
		return true;
	}
	
	public function _clearTemporaryDir(){
		if($this->_temporaryDir != '' && is_dir($this->_temporaryDir)){
			
			$d = dir($this->_temporaryDir);
			
			while (false !== ($entry = $d->read())) {
			  	
				if(substr($entry, 0, 1) == '.'){
					continue;
				}
						
				if(is_file($d->path . $entry) && filemtime($d->path . $entry) + 7200 < time()){
					unlink($d->path . $entry);
				}
			}
			$d->close();
		}
	}
	
	function __destruct(){
		
		$this->_clearTemporaryDir();
	}
	
}